#include "http.h"

CHttpBuffer::CHttpBuffer(const char* name)
: CByteBuffer(name)
{
}

CHttpBuffer::CHttpBuffer(const char* inbuf, ushort inlen, const char* name)
: CByteBuffer(inbuf, inlen, name)
{
}

int CHttpBuffer::ToBlock(CByteBlock* block)
{
    ushort orgsrclen = GetSize();
    const char* orgsrc = GetByte();

	const char* pcur = orgsrc;
	const char* pend = orgsrc + orgsrclen;
                    
    ushort labelsize = 0;
    const char* label = NULL;
    int contentlength = -1;
    
    while(pcur < pend)
	{
		const char* plast = pcur;

		if(!FindLabel(pcur, (ushort)(pend-pcur), label, labelsize))
            break;

        if(label >= pend) return 0;

        switch(*label)
        {
        case '\n'://node ending
            {
                CByteBlock* child;
                
                if(block->IsEmpty())
                    child = AddChild<CHttpBlock>(block, HTTP_SIGN_LINE);
                else if(1 != labelsize)
                    child = AddChild<CHttpBlock>(block, HTTP_SIGN_HEADER);
                else
                    child = AddChild<CHttpBlock>(block, HTTP_SIGN_BODY);

                if(NULL == child) return false;

                if(1 == labelsize)//the end
                {
                    ushort size = (ushort)(pend - pcur);

                    if(contentlength >= 0)
                    {
                        if(contentlength > size)
                            return 0;
                        
                        if(0 != contentlength)
                            child->SetBlockValue(pcur, contentlength);

                        return label + contentlength + 1 - orgsrc;
                    }
                    else // for chucked
                    {
                        return -1;
                    }
                }
                else
                {
                    if(!ParseLabel(child, plast, labelsize))
                        return false;

                    if(0 == strnicmp(child->GetBlockName().c_str(), "Content-Length", 14))
                        contentlength = atoi(child->GetBlockValue().c_str());
                }
            }
			break;

		default:
            return false;
		}
	}

	return 0;
}

bool CHttpBuffer::FindLabel(const char* &begin, ushort len, const char* &label, ushort &labelsize)
{
	const char* left = (const char*)memchr(begin, '\r', len);

	if(NULL == left) return false;

	label = left + 1;// len -= (ushort)(label - begin);

	labelsize = (ushort)(label - begin);

	begin = label + 1;

	return true;
}

bool CHttpBuffer::ParseLabel(CByteBlock* block, const char* label, ushort labelsize)
{
    if(HTTP_SIGN_LINE == block->GetBlockSign())
    {
        if(block->SetBlockName(label, labelsize) <= 0)
            return false;
    }
    else
    {
        const char* left = (const char*)memchr(label, ':', labelsize);

        if(block->SetBlockName(label, ((ushort)(left-label))) <= 0)
            return false;
        
        if(block->SetBlockValue(left+2, (ushort)(label+labelsize-left-2)) <= 0)
            return false;
    }

    return true;
}

CHttpBlock::CHttpBlock(const char* blockname)
: CByteBlock(blockname)
{
    m_seperator = "";
}

CHttpBlock::CHttpBlock(uchar sign, CByteBlock* parent)
: CByteBlock(sign, parent)
{
    m_seperator = "";
}

int CHttpBlock::ToBuffer(CByteBuffer* buffer)
{
    string line; string header; string body;

    if(HTTP_SIGN_ROOT != GetBlockSign()) return -1;

    int index = 1;

    CByteBlock* child = GetNextChild(index);

    while(NULL != child)
    {
        string name = child->GetBlockName(); string value = child->GetBlockValue();

        if(HTTP_SIGN_LINE == child->GetBlockSign() && !name.empty())
        {
            if(!line.empty())
                return -1;
            else
            {
                line = name; line += "\r\n";
            }
        }
        else if(HTTP_SIGN_HEADER == child->GetBlockSign())
        {
            if(name.empty() || value.empty()) return -1;

            if(0 != strnicmp(name.c_str(), "Content-Length", 14))
            {
                header += name; header += ": ";
                header += value; header += "\r\n";
            }
        }
        else if(HTTP_SIGN_BODY == child->GetBlockSign())
        {
            if(value.empty()) return -1;

            if(!body.empty()) body += m_seperator;

            body += value;
        }

        child = GetNextChild(++index);
    }

    if(line.empty() || header.empty()) return -1;

    char tostring[50] = { 0 }; snprintf(tostring, 50, "%u", body.size());

    header += "Content-Length: "; header += tostring; header += "\r\n";

    buffer->Write(line.c_str(), (ushort)(line.size()));

    buffer->Write(header.c_str(), (ushort)(header.size())); buffer->Write("\r\n", 2);
    
    if(!body.empty()) buffer->Write(body.c_str(), (ushort)(body.size()));

    return 1;
}

bool de_http_line(CByteBlock& block, HttpLine& httpline)
{
    CByteBlock* child = block.GetNextChild();

    if(NULL == child || HTTP_SIGN_LINE != child->GetBlockSign() || child->GetBlockName().empty())
        return false;

    string line = child->GetBlockName();

    string::size_type size = line.find(" ");
    if(string::npos == size) return false;
    else httpline._method = line.substr(0, size);

    if(httpline._method.empty())
        return false;

    line.erase(0, ++size); size = line.find(" ");
    if(string::npos == size) return false;
    else httpline._url = line.substr(0, size);

    if(httpline._url.empty())
        return false;

    line.erase(0, ++size); httpline._version = line;
    if(httpline._version.empty())
        return false;

    return true;
}

bool en_http_line(CByteBlock& block, HttpLine& httpline)
{
    if(httpline._method.empty() || httpline._url.empty() || httpline._version.empty())
        return false;

    string line = httpline._method; line += " ";
    line += httpline._url; line += " ";
    line += httpline._version;

    CByteBlock* child = AddChild<CHttpBlock>(&block, HTTP_SIGN_LINE);

    if(NULL != child)
    {
        if(child->SetBlockName(line.c_str(), (ushort)(line.size())) <= 0)
            return false;

        return true;
    }

    return false;
}

bool en_http_header(CByteBlock& block, string name, string value)
{
    CByteBlock* child = AddChild<CHttpBlock>(&block, HTTP_SIGN_HEADER);

    if(NULL == child) return false;

    if(child->SetBlockName(name) <= 0)
        return false;

    if(child->SetBlockValue(value) <= 0)
        return false;

    return true;
}

bool en_http_body(CByteBlock& block, string body)
{
    CByteBlock* child = AddChild<CHttpBlock>(&block, HTTP_SIGN_BODY);

    if(NULL == child) return false;

    if(child->SetBlockValue(body) <= 0)
        return false;

    return true;
}
